/*
 ==============================================================================
 
 DelayLine.cpp
 Created: 30 Nov 2014 6:09:37pm
 Author:  Alistair Barker
 
 ==============================================================================
 */

#include "DelayLine.h"

DelayLine::DelayLine()
{
    delayBuffer.setSize(1, 4096);
    
    for (int i = 0; i < delayBuffer.getNumSamples(); i++) {
        delayBuffer.setSample(0, i, 0.0);
    }
    
    writePointer = 0;
    newAllPassReader = new AllPassReader(delayBuffer);
    oldAllPassReader = new AllPassReader(delayBuffer);
    
    glissandoIncrement = 0.0;
    delayLengthSamples = 0.0;
    
}

void DelayLine::setDelay(double delayInSamples)
{
    
    newAllPassReader->setDelayLength(delayInSamples, writePointer);
    delayLengthSamples = delayInSamples;
    crossfadeCount = 1;
    
    AllPassReader *tempAllPassReader;       //Swap all pass readers
    tempAllPassReader = newAllPassReader;
    newAllPassReader = oldAllPassReader;
    oldAllPassReader = tempAllPassReader;

}

void DelayLine::setDelayWithGliss(double newDelayInSamples, double timeInSecs, double fs)
{
    if (newDelayInSamples != delayLengthSamples)
    {
        if (timeInSecs > 0.0 && delayLengthSamples != 0.0)
        {
            glissandoIncrement =  16 * (newDelayInSamples - delayLengthSamples)/(timeInSecs * fs);
            glissandoTarget = newDelayInSamples;
            crossfadeCount = 1;
        }
        else
        {
            setDelay(newDelayInSamples);
        }
    }
}

double DelayLine::read()
{
    double output;          //init OP
    
    if (crossfadeCount == 0) //No crossfade - just send output
    {
        output = oldAllPassReader->calculateOutput();
    }
    else if (crossfadeCount > 0 && crossfadeCount < 6) // Starting cross fade - Warm up new allpassreader, but only send old output.
    {
        output = oldAllPassReader->calculateOutput();
        newAllPassReader->calculateOutput();
        crossfadeCount++;
    }
    else if (crossfadeCount >= 6 ) //Cross fade between old and new all pass readers
    {
        double crossFade = (crossfadeCount - 5)/crossfadeLength;
        output = oldAllPassReader->calculateOutput()*(1-crossFade) + newAllPassReader->calculateOutput()*(crossFade);
        crossfadeCount++;
    }
    
    if (crossfadeCount == crossfadeLength)
    {
        crossfadeCount = 0;
        
        if (glissandoIncrement != 0.0)  //If currently glissing, check whether target reached and set new delay.
        {
            double newDelay = delayLengthSamples + glissandoIncrement;
            if ((glissandoIncrement > 0 && newDelay > glissandoTarget) || (glissandoIncrement < 0 && newDelay < glissandoTarget))
            {
                newDelay = glissandoTarget;
                glissandoIncrement = 0.0;
            }
            setDelay(newDelay);
        }
    }
    
    return output;
}

void DelayLine::write(double inputSample)
{
    delayBuffer.setSample(0, writePointer, inputSample);
    writePointer++;
    
    if (writePointer >= delayBuffer.getNumSamples()) writePointer -= delayBuffer.getNumSamples();    //ensure write pointers are looped
    
}

void AllPassReader::advanceReader()
{
    readPointer++;
    checkReadbuffers();
}


void AllPassReader::setDelayLength(double delayInSamples, int writePointer)
{
    int intDelay = floor(delayInSamples) ;
    fractDelay = delayInSamples - intDelay;
    readPointer = writePointer - intDelay + 1;
    checkReadbuffers();
}

void AllPassReader::checkReadbuffers()
{
    while (readPointer >= delayBuffer->getNumSamples())
    {
        readPointer -= delayBuffer->getNumSamples();
    }
    
    while(readPointer < 0)
    {
        readPointer += delayBuffer->getNumSamples();
    }
}



AllPassReader::AllPassReader(AudioSampleBuffer &buffer)
{
    delayBuffer = &buffer;
    lastSample = 0.0f;
    fractDelay = 0.0f;
    readPointer = 0;
}

double AllPassReader::calculateOutput()
{
    
    int readPtrMinusOne = (readPointer - 1);
    if ( (readPointer - 1) < 0) readPtrMinusOne += delayBuffer->getNumSamples();
    
    float a = (1-fractDelay)/(1+fractDelay);
    
    double output = a*(delayBuffer->getSample(0, readPointer)) + delayBuffer->getSample(0, readPtrMinusOne) - a * lastSample; //all-pass
    
    lastSample = output;
    advanceReader();
    
    return output;
}